package gdma.hqdes.article.renderer;

import gdma.hqdes.bean.HqdIndex;
import lombok.Data;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;

public class UnitScoreTableExcel {

    private int maxLevel = 3;

    public UnitScoreTableExcel(int maxLevel) {
        this.maxLevel = maxLevel;
    }

    public void render(HqdIndex root, OutputStream output, BiFunction<Cell, HqdIndex, Integer> writeRow, Function<Cell, Integer> writeHeader) throws IOException {
        if (root == null) return;
        try(XSSFWorkbook workbook = new XSSFWorkbook()) {
            XSSFSheet sheet = workbook.createSheet("Unit Scores");
            int n = 0;
            Row row;
            Cell startCell;
            if(writeHeader != null) {
                row = getOrCreateRow(sheet, n);
                startCell = row.getCell(0, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
                writeHeader.apply(startCell);
                n++;
            }
            row = getOrCreateRow(sheet, n);
            startCell = row.getCell(0, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
            writeRow.apply(startCell, root);
            n++;
            getOrCreateRow(sheet, n);
            n++;
            if(writeHeader != null) {
                int col = 0;
                row = getOrCreateRow(sheet, n);
                for(int i = 0; i < maxLevel; i++) {
                    startCell = row.getCell(col, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
                    col += writeHeader.apply(startCell);
                }
                n++;
            }
            renderRows(root.getChildren(), sheet, n, 0, writeRow);
            workbook.write(output);
        }
    }

    public void render(HqdIndex root, OutputStream output) throws IOException {
        render(root, output, WRITE_SINGLE_SCORE, WRITE_HEADER);
    }

    public static BiFunction<Cell, HqdIndex, Integer> WRITE_SINGLE_SCORE = (startCell, index) -> {
        Row row = startCell.getRow();
        int startCol = startCell.getColumnIndex();
        Cell indexCell = row.getCell(startCol, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
        indexCell.setCellValue("(" + index.getCode() + ")" + index.getName());
        Cell scoreCell = row.getCell(startCol + 1, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
        scoreCell.setCellValue(index.getUnitScore() != null ? index.getUnitScore().getScore() : 0.0);
        return 2;
    };

    public static Function<Cell, Integer> WRITE_HEADER = startCell -> {
        Row row = startCell.getRow();
        int startCol = startCell.getColumnIndex();
        Cell indexCell = row.getCell(startCol, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
        indexCell.setCellValue("指标");
        Cell scoreCell = row.getCell(startCol + 1, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
        scoreCell.setCellValue("得分");
        return 2;
    };

    private int renderRows(List<HqdIndex> indexScores, Sheet sheet, int startRow, int startCol, BiFunction<Cell, HqdIndex, Integer> writeRow) {
        int endRow = startRow;
        for (HqdIndex index : indexScores) {
            startRow = endRow;
            Row row = getOrCreateRow(sheet, startRow);
            Cell startCell = row.getCell(startCol, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
            int cols = writeRow.apply(startCell, index);
            endRow = !CollectionUtils.isEmpty(index.getChildren()) && index.getLevel() < maxLevel ?
                    renderRows(index.getChildren(), sheet, startRow, startCol + cols, writeRow) :
                    startRow + 1;
            if (endRow - startRow > 1) {
                for(int i = 0; i < cols; i++) {
                    mergeCellsVertically(sheet, startCol + i, startRow, endRow - 1);
                }
            }
        }
        return endRow;
    }

    private Row getOrCreateRow(Sheet sheet, int pos) {
        Row row = sheet.getRow(pos);
        if (row == null) {
            row = sheet.createRow(pos);
        }
        return row;
    }

    private void mergeCellsVertically(Sheet sheet, int col, int startRow, int endRow) {
        sheet.addMergedRegion(new CellRangeAddress(startRow, endRow, col, col));
    }

    @Data
    public static class CompositeIndex extends HqdIndex {
        List<Float> scores = new ArrayList<>();
    }

    public static BiFunction<Cell, HqdIndex, Integer> WRITE_COMPOSITE_SCORE = (startCell, idx) -> {
        Row row = startCell.getRow();
        int startCol = startCell.getColumnIndex();
        CompositeIndex index = (CompositeIndex)idx;
        int n = 0;
        Cell indexCell = row.getCell(startCol + n, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
        indexCell.setCellValue("(" + index.getCode() + ")" + index.getName());
        n++;
        List<Float> scores = index.getScores();
        if(scores.size() > 0) {
            Cell scoreCell = row.getCell(startCol + n, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
            scoreCell.setCellValue(scores.get(0));
            n++;
        }

        Workbook workbook = startCell.getSheet().getWorkbook();
        CellStyle percentStyle = workbook.createCellStyle();
        DataFormat format = workbook.createDataFormat();
        percentStyle.setDataFormat(format.getFormat("0.00%"));
        for(int i = 1; i < scores.size(); i++) {
            Cell scoreCell = row.getCell(startCol + n, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
            scoreCell.setCellValue(scores.get(i));
            n++;
            scoreCell = row.getCell(startCol + n, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
            float rate = (scores.get(0) - scores.get(i)) / scores.get(i);
            scoreCell.setCellValue(rate);
            scoreCell.setCellStyle(percentStyle);
            n++;
        }
        return n;
    };

}
